package com.asiainfo.exec.core;

import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;

public class CmdLineParser {

    private final List<String> optionPrefixes;
    private Map<String, List<String>> options = new HashMap<>();

    public CmdLineParser(String... optionPrefixes) {
        this.optionPrefixes = Arrays.asList(optionPrefixes);
    }

    static class CmdLineParserContext {
        private final String[] args;

        private int pos;

        public CmdLineParserContext(String... args) {
            this.args = args;
            pos = 0;
        }

        protected boolean hasMore() {
            return pos < args.length;
        }

        protected String getCurrentToken() {
            return args[pos];
        }

        private void proceed(int n) {
            pos += n;
        }

        public String getParameter(int idx) {
            if (pos + idx >= args.length || pos + idx < 0)
                throw new IllegalArgumentException();
            return args[pos + idx];
        }

        public int size() {
            return args.length - pos;
        }

        /**
         * Used when the current token is of the form "-option=value",
         * to replace the current token by "value", as if this was given as two tokens "-option value"
         */
        String splitToken() {
            if (pos < args.length && pos >= 0) {
                int idx = args[pos].indexOf("=");
                String option = args[pos].substring(0, idx);
                if (idx > 0) {
                    args[pos] = args[pos].substring(idx + 1);
                }
                return option;
            }
            return null;
        }
    }

    public void doParser(String... args) {
        CmdLineParserContext ctx = new CmdLineParserContext(args);
        String currKey = null;
        while (ctx.hasMore()) {
            String arg = ctx.getCurrentToken();
            if (isOption(arg)) {
                String optionPrefix = arg.replaceAll("^(-+).*$", "$1");
                if (optionPrefixes.contains(optionPrefix)) {
                    boolean isKeyValuePair = arg.indexOf('=') != -1;
                    if (isKeyValuePair) {
                        currKey = ctx.splitToken();
                        String value = ctx.getCurrentToken();
                        addOptionValue(currKey, value);
                    } else {
                        currKey = ctx.getCurrentToken();
                        addOptionValue(currKey, null);
                    }
                } else {
                    currKey = null;
                }
            } else {
                String value = ctx.getCurrentToken();
                if (!StringUtils.isEmpty(currKey)) {
                    addOptionValue(currKey, value);
                }
            }
            ctx.proceed(1);
        }
    }

    private boolean isNotOption(String arg) {
        return !arg.startsWith("-");
    }

    private boolean isOption(String arg) {
        return arg.startsWith("-");
    }

    public Map<String, List<String>> getOptions() {
        return options;
    }

    public String[] getArgs() {
        if (CollectionUtils.isEmpty(options)) {
            return new String[0];
        }
        List<String> res = new ArrayList<>();
        for (Map.Entry<String, List<String>> option : options.entrySet()) {
            String key = option.getKey();
            res.add(key);
            List<String> values = option.getValue();
            if (!CollectionUtils.isEmpty(values)) {
                res.addAll(values);
            }
        }
        String[] optionsStringArray = res.toArray(new String[res.size()]);
        return optionsStringArray;
    }

    private void addOptionValue(String currKey, String value) {
        List<String> values;
        if ((values = options.get(currKey)) == null) {
            values = new ArrayList<>();
        }
        if (!StringUtils.isEmpty(value)) {
            values.add(value);
        }
        options.put(currKey, values);
    }

}